#!/usr/bin/env node
const Koa = require('koa')
const send = require('koa-send')
const path = require('path')
const { Readable } = require('stream')
const compilerSFC = require('@vue/compiler-sfc')

const app = new Koa()

const streamToString = stream => new Promise((resolve, reject) => {
    const chunks = []
    // 将读取到的内容存储到 chunks 数组中
    stream.on('data', chunk => chunks.push(chunk))
    stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
    stream.on('error', reject)
})

const stringToStream = text => {
    const stream = new Readable()
    stream.push(text)
    stream.push(null)
    return stream
}

// 3. 加载第三方模块
app.use(async (ctx, next) => {
    // ctx.path --> /@modules/vue
    if (ctx.path.startsWith('/@modules/')) {
        const moduleName = ctx.path.substr(10)
        // 拼接路径
        const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
        // 模块的 package.json 文件
        const pkg = require(pkgPath)
        // 修改路径为 package.json 中的 module 文件路径
        ctx.path = path.join('/node_modules', moduleName, pkg.module)
    }
    await next()
})

// 1. 静态文件服务器
app.use(async (ctx, next) => {
    await send(ctx, ctx.path, { root: process.cwd(), index: 'index.html' })
    await next()
})

// 4. 处理单文件组件
app.use(async (ctx, next) => {
    if (ctx.path.endsWith('.vue')) {
        const contents = await streamToString(ctx.body) // 文件流
        // 使用 @vue/compiler-sfc 插件对单文件组件进行编译
        // parse 会返回一个对象，包含 descriptor 描述对象和编译错误
        const { descriptor } = compilerSFC.parse(contents)
        let code 
        // 没有 type 属性说明是第一次请求
        if (!ctx.query.type) {
            code = descriptor.script.content
            // console.log(code)
            code = code.replace(/export\s+default\s+/g, 'const __script = ')
            code += `
            import { render as __render } from "${ctx.path}?type=template"
            __script.render = __render
            export default __script
            `
        } else if (ctx.query.type === 'template') {
            // 第二次请求 使用 @vue/compiler-sfc 编译模板文件
            const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content })
            // 将编译后的文件保存
            code = templateRender.code
        }
        // 将文件类型设置成 JavaScript
        ctx.type = 'application/javascript'
        // 将文本转换成流交给下一个中间件处理
        ctx.body = stringToStream(code)
    }
    await next()
})

// 2. 修改第三方模块的路径
app.use(async (ctx, next) => {
    if (ctx.type === 'application/javascript') {
        const contents = await streamToString(ctx.body)
        // import vue from 'vue' => import vue from '/@modules/vue'
        // import App from './App.vue'
        ctx.body = contents
            .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
            .replace(/process\.env\.NODE_ENV/g, '"development"')

    }
})



app.listen(3000)
console.log('serve runing http://localhost:3000')











